package cn.gemiman.daoyi.module.netty.server.processor;

import cn.gemiman.daoyi.framework.common.pojo.CommonResult;
import cn.gemiman.daoyi.module.netty.contant.ChannelAttrKey;
import cn.gemiman.daoyi.module.netty.contant.NettyConstant;
import cn.gemiman.daoyi.module.netty.contant.NettyRedisKey;
import cn.gemiman.daoyi.module.netty.enums.NettyCmdType;
import cn.gemiman.daoyi.module.netty.model.NettyLoginInfo;
import cn.gemiman.daoyi.module.netty.model.NettySendInfo;
import cn.gemiman.daoyi.module.netty.model.NettySessionInfo;
import cn.gemiman.daoyi.module.netty.server.NettyServerGroup;
import cn.gemiman.daoyi.module.netty.server.UserChannelCtxMap;
import cn.gemiman.daoyi.module.system.api.oauth2.OAuth2TokenApi;
import cn.gemiman.daoyi.module.system.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.AttributeKey;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Slf4j
@Component
@RequiredArgsConstructor
public class LoginProcessor extends AbstractMessageProcessor<NettyLoginInfo> {

    private final RedisTemplate<String, Object> redisTemplate;
    private final OAuth2TokenApi oauth2TokenApi;

    @Override
    public synchronized void process(ChannelHandlerContext ctx, NettyLoginInfo loginInfo) {
        // 校验访问令牌
        CommonResult<OAuth2AccessTokenCheckRespDTO> tokenRes = oauth2TokenApi.checkAccessToken(loginInfo.getAccessToken());
        if (!tokenRes.isSuccess()) {
            ctx.channel().close();
            log.warn("用户token校验不通过，强制下线,token:{}", loginInfo.getAccessToken());
        }
        OAuth2AccessTokenCheckRespDTO accessToken = tokenRes.getCheckedData();
        NettySessionInfo sessionInfo = BeanUtil.copyProperties(accessToken, NettySessionInfo.class).setTerminal(loginInfo.getTerminal());
        Long userId = sessionInfo.getUserId();
        Integer terminal = sessionInfo.getTerminal();
        log.info("用户登录，userId:{}", userId);
        ChannelHandlerContext context = UserChannelCtxMap.getChannelCtx(userId, terminal);
        if (context != null && !ctx.channel().id().equals(context.channel().id())) {
            // 不允许多地登录,强制下线
            NettySendInfo<Object> sendInfo = new NettySendInfo<>();
            sendInfo.setCmd(NettyCmdType.FORCE_LOGUT.code());
            sendInfo.setData("您已在其他地方登陆，将被强制下线");
            context.channel().writeAndFlush(sendInfo);
            log.info("异地登录，强制下线,userId:{}", userId);
        }
        // 绑定用户和channel
        UserChannelCtxMap.addChannelCtx(userId, terminal, ctx);
        // 设置用户id属性
        AttributeKey<Long> userIdAttr = AttributeKey.valueOf(ChannelAttrKey.USER_ID);
        ctx.channel().attr(userIdAttr).set(userId);
        // 设置用户终端类型
        AttributeKey<Integer> terminalAttr = AttributeKey.valueOf(ChannelAttrKey.TERMINAL_TYPE);
        ctx.channel().attr(terminalAttr).set(terminal);
        // 初始化心跳次数
        AttributeKey<Long> heartBeatAttr = AttributeKey.valueOf("HEARTBEAt_TIMES");
        ctx.channel().attr(heartBeatAttr).set(0L);
        // 在redis上记录每个user的channelId，15秒没有心跳，则自动过期
        String key = String.join(":", NettyRedisKey.IM_USER_SERVER_ID, userId.toString(), terminal.toString());
        redisTemplate.opsForValue().set(key, NettyServerGroup.serverId, NettyConstant.ONLINE_TIMEOUT_SECOND, TimeUnit.SECONDS);
        // 响应ws
        NettySendInfo<Object> sendInfo = new NettySendInfo<>();
        sendInfo.setCmd(NettyCmdType.LOGIN.code());
        ctx.channel().writeAndFlush(sendInfo);
    }


    @Override
    public NettyLoginInfo transForm(Object o) {
        return JSONUtil.toBean(JSONUtil.toJsonStr(o), NettyLoginInfo.class);
    }
}
